﻿using System;
using System.Collections.Generic;
using System.Linq;
using GIMS.Def;
using GIMS.GUI.GraphItems;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using GIMS.Def.Arguments;

namespace GIMS.GUI.Test
{
    /// <summary>
    ///This is a test class for CustomGraphTest and is intended
    ///to contain all CustomGraphTest Unit Tests
    ///</summary>
    [TestClass()]
    public class CustomGraphTest
    {
        /// <summary>
        ///A test for AddNewEdgeBetween with a circle
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithCircle()
        {
            CustomVertex target1;
            var target = CreateDefaultCustomGraph(out target1);
            CustomVertex source = target.Vertices.First(p => p.Name == "Save");
            bool expected = false;
            bool actual;
            actual = target.AddNewEdgeBetween(source, target1);
            Assert.AreEqual(expected, actual);
            Assert.AreEqual(5, target.Edges.Count());
        }

        /// <summary>
        ///A test for AddNewEdgeBetween without a circle
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithoutCircle()
        {
            CustomVertex source;
            var target = CreateDefaultCustomGraph(out source);
            CustomVertex target1 = new CustomVertex(OperatorFactory.GetOperator("save"));
            target.AddVertex(target1);
            bool expected = true;
            bool actual;
            actual = target.AddNewEdgeBetween(source, target1);
            Assert.AreEqual(6, target.Edges.Count());
            Assert.IsTrue(target.Edges.Count(p => p.Source == source && p.Target == target1) == 1);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for AddNewEdgeBetween without a self-referencing vertex
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithSelfRerference()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex source = target.Vertices.First(p => p.Name == "Resize");
            CustomVertex target1 = source;
            target.AddVertex(target1);
            bool expected = false;
            bool actual;
            actual = target.AddNewEdgeBetween(source, target1);
            Assert.AreEqual(5, target.Edges.Count());
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for AddNewEdgeBetween with nonexistent target
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithNonexistentSource()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex source = target.Vertices.First(p => p.Name == "Save");
            CustomVertex target1 = new CustomVertex(null);
            bool expected = false;
            bool actual;
            actual = target.AddNewEdgeBetween(source, target1);
            Assert.AreEqual(expected, actual);
            Assert.AreEqual(5, target.Edges.Count());
        }

        /// <summary>
        ///A test for AddNewEdgeBetween with nonexistent source
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithNonexistentTarget()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex source = new CustomVertex(null);
            CustomVertex target1 = target.Vertices.First(p => p.Name == "Save");
            bool expected = false;
            bool actual;
            actual = target.AddNewEdgeBetween(source, target1);
            Assert.AreEqual(expected, actual);
            Assert.AreEqual(5, target.Edges.Count());
        }

        /// <summary>
        ///A test for AddNewEdgeBetween with an existing sequence number
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewEdgeBetweenTestWithExistingSequenceNumber()
        {
            var target = CreateTwoVerticesCustomGraph();
            target.DeleteEdge(target.Edges.Single()); 
            var loadVertex = target.Vertices.First(p => p.Name == "Load");
            var saveVertex = target.Vertices.First(p => p.Name == "Save");
            bool added = target.AddNewEdgeBetween(loadVertex, saveVertex, 5);
            Assert.AreEqual(true, added);
            Assert.AreEqual(5, target.Edges.Single().Sequence);
        }

        /// <summary>
        ///A test for AddNewVertexBetween.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void AddNewVertexBetweenTest()
        {
            var target = CreateTwoVerticesCustomGraph();
            var newVertex = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var loadVertex = target.Vertices.First(p => p.Name == "Load");
            var saveVertex = target.Vertices.First(p => p.Name == "Save");
            target.AddNewVertexBetween(loadVertex, saveVertex, newVertex);
            Assert.AreEqual(2, target.Edges.Count());
            Assert.AreEqual(3, target.Vertices.Count());
        }

        /// <summary>
        ///A test for getChilds with elements
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void GetChildsTestWithElements()
        {
            CustomVertex parent;
            var target = CreateDefaultCustomGraph(out parent);
            IEnumerable<CustomVertex> actual;
            actual = target.GetChilds(parent);
            Assert.IsNotNull(actual);
            Assert.AreEqual(2, actual.Count());
            Assert.IsTrue(actual.Contains(target.Vertices.First(p => p.Name == "Resize")));
            Assert.IsTrue(actual.Contains(target.Vertices.First(p => p.Name == "Save")));
        }

        /// <summary>
        ///A test for getChilds without elements
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void GetChildsTestWithoutElements()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex parent = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            IEnumerable<CustomVertex> actual;
            actual = target.GetChilds(parent);
            Assert.IsNotNull(actual);
            Assert.AreEqual(0, actual.Count());
        }

        /// <summary>
        ///A test for existsPath with an existent path
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void ExistsPathTestWithPath()
        {
            CustomVertex from;
            CustomVertex save;
            var target = CreateDefaultCustomGraphAccessor(out from, out save);
            bool expected = true;
            bool actual;
            actual = target.ExistsPath(from, save);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for existsPath without an existent path
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void ExistsPathTestWithoutPath()
        {
            var target = new CustomGraph_Accessor();

            var v1 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v2 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v3 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v4 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v5 = new CustomVertex(OperatorFactory.GetOperator("Save"));

            target.AddNewVertex(v1);
            target.AddNewVertexToSource(v1, v2);
            target.AddNewVertexToSource(v2, v3);
            target.AddNewVertexToSource(v3, v5);

            target.AddNewSourceToVertex(v5, v4);

            target.AddNewEdgeBetween(v1, v5);

            bool expected = false;
            bool actual;
            actual = target.ExistsPath(v1, v4);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for IsGraphEmpty
        ///</summary>
        [TestMethod()]
        public void IsGraphEmptyTest()
        {
            CustomGraph target = new CustomGraph();
            bool actual = target.IsGraphEmpty;
            Assert.AreEqual(true, actual);

            target.AddNewVertex(new CustomVertex(OperatorFactory.GetOperator("Load")));
            actual = target.IsGraphEmpty;
            Assert.AreEqual(false, actual);
        }

        /// <summary>
        ///A test for GetParents, validates the sequence too.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void GetParentsTest()
        {
            CustomVertex load;
            CustomVertex save;
            var target = CreateDefaultCustomGraphAccessor(out load, out save);

            var actual = target.GetParents(save).ToList();
            Assert.IsNotNull(actual);
            Assert.AreEqual(3, actual.Count());

            Assert.AreEqual("Resize", actual[0].Name);
            Assert.AreEqual("Load", actual[1].Name);
            Assert.AreEqual("Load", actual[2].Name);
        }

        /// <summary>
        ///A test for GetSave.
        ///</summary>
        [TestMethod()]
        public void GetSaveTestWithSave()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex expected = target.Vertices.First(p => p.Name == "Save");
            CustomVertex actual;
            actual = target.GetSave();
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for GetSave.
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetSaveTestWithoutSave()
        {
            var target = new CustomGraph();
            InvalidOperationException exception = null;
            try
            {
                var saveVertex = target.GetSave();
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        ///A test for GetParentsCount with 3 parents
        ///</summary>
        [TestMethod()]
        public void GetParentsCountTestWithThreeParents()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex saveVertex = target.GetSave();
            int expected = 3;
            int actual;
            actual = target.GetParentsCount(saveVertex);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for GetParentsCount without parents.
        ///</summary>
        [TestMethod()]
        public void GetParentsCountTestWithoutParents()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex loadVertex = target.Vertices.First(p => p.Name == "Load");
            int expected = 0;
            int actual;
            actual = target.GetParentsCount(loadVertex);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for HasParents with parents
        ///</summary>
        [TestMethod()]
        public void HasParentsTestWithParents()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex saveVertex = target.GetSave();
            bool expected = true;
            bool actual;
            actual = target.HasParents(saveVertex);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for HasParents without parents.
        ///</summary>
        [TestMethod()]
        public void HasParentsTestWithoutParents()
        {
            var target = CreateDefaultCustomGraph();
            CustomVertex loadVertex = target.Vertices.First(p => p.Name == "Load");
            bool expected = false;
            bool actual;
            actual = target.HasParents(loadVertex);
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for DeleteVertex
        ///</summary>
        [TestMethod()]
        public void DeleteVertexTest()
        {
            CustomVertex firstLoad;
            var target = CreateDefaultCustomGraph(out firstLoad);
            target.DeleteVertex(firstLoad);
            Assert.AreEqual(4, target.Vertices.Count());
        }

        /// <summary>
        ///A test for DeleteEdge
        ///</summary>
        [TestMethod()]
        public void DeleteEdgeTest()
        {
            CustomVertex firstLoad;
            var target = CreateDefaultCustomGraph(out firstLoad);
            var edge = target.Edges.First(p => p.Source == firstLoad);
            target.DeleteEdge(edge);
            Assert.AreEqual(5, target.Vertices.Count());
            Assert.AreEqual(4, target.Edges.Count());
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete possible because it is the first vertex.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest1()
        {
            var target = CreateThreeVerticesCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Load"));
            Assert.AreEqual(true, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete not possible because it would split the graph.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest2()
        {
            var target = CreateThreeVerticesCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Append"));
            Assert.AreEqual(false, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete possible because it is the last vertex.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest3()
        {
            var target = CreateThreeVerticesCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Save"));
            Assert.AreEqual(true, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete not possible because it would split the graph.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest4()
        {
            var target = CreateTwoChildsCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Load"));
            Assert.AreEqual(false, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete possible because its a last vertex with only one parent.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest5()
        {
            var target = CreateTwoChildsCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Append"));
            Assert.AreEqual(true, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete possible because its a first vertex with only one child.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest6()
        {
            var target = CreateTwoParentsCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Load"));
            Assert.AreEqual(true, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete not possible because it would split the graph.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest7()
        {
            var target = CreateTwoParentsCustomGraph();
            var actual = target.CanDeleteVertex(target.Vertices.First(p => p.Name == "Append"));
            Assert.AreEqual(false, actual);
        }

        /// <summary>
        ///A test for CanDeleteVertex. Delete possible because it is the only vertex.
        ///</summary>
        [TestMethod()]
        public void CanDeleteVertexTest8()
        {
            var target = new CustomGraph();
            target.AddNewVertex(new CustomVertex(OperatorFactory.GetOperator("Load")));
            var actual = target.CanDeleteVertex(target.Vertices.First());
            Assert.AreEqual(true, actual);
        }

        /// <summary>
        ///A test for Clear
        ///</summary>
        [TestMethod()]
        public void ClearTest()
        {
            CustomGraph target = CreateDefaultCustomGraph();
            target.Clear();
            Assert.AreEqual(0, target.Vertices.Count());
            Assert.AreEqual(0, target.Edges.Count());
        }

        /// <summary>
        ///A test for UnsetOtherIsParameter.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("GIMS.GUI.exe")]
        public void UnsetOtherIsParameterTest()
        {
            var target = new CustomGraph();
            var load1 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var load2 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var append = new CustomVertex(OperatorFactory.GetOperator("Append"));
            var save = new CustomVertex(OperatorFactory.GetOperator("Save"));
            target.AddNewVertex(load1);
            target.AddNewVertexToSource(load1, append);
            target.AddNewVertexToSource(append, save);
            target.AddNewSourceToVertex(append, load2);

            ((PathArgument)load1.IMOperator.Argument).IsParameter = true;
            Assert.IsTrue(((PathArgument)load1.IMOperator.Argument).IsParameter);
            Assert.IsFalse(((PathArgument)load2.IMOperator.Argument).IsParameter);
            Assert.IsFalse(((PathArgument)save.IMOperator.Argument).IsParameter);

            ((PathArgument)load2.IMOperator.Argument).IsParameter = true;
            Assert.IsFalse(((PathArgument)load1.IMOperator.Argument).IsParameter);
            Assert.IsTrue(((PathArgument)load2.IMOperator.Argument).IsParameter);
            Assert.IsFalse(((PathArgument)save.IMOperator.Argument).IsParameter);

            ((PathArgument)save.IMOperator.Argument).IsParameter = true;
            Assert.IsFalse(((PathArgument)load1.IMOperator.Argument).IsParameter);
            Assert.IsTrue(((PathArgument)load2.IMOperator.Argument).IsParameter);
            Assert.IsTrue(((PathArgument)save.IMOperator.Argument).IsParameter);
        }

    
        /// <summary>
        ///A test for GetLast
        ///</summary>
        [TestMethod()]
        public void GetLastTestSuccess()
        {
            CustomGraph target = CreateDefaultCustomGraph();
            var last = target.GetLast();
            Assert.AreEqual("Save", last.Name);
        }

        #region Helper Methods

        private CustomGraph CreateDefaultCustomGraph()
        {
            CustomVertex crap;
            return CreateDefaultCustomGraph(out crap);
        }

        private CustomGraph CreateDefaultCustomGraph(out CustomVertex firstLoad)
        {
            var graph = new CustomGraph();

            var v1 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v2 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v3 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v4 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v5 = new CustomVertex(OperatorFactory.GetOperator("Save"));

            graph.AddNewVertex(v1);
            graph.AddNewVertexToSource(v1, v2);
            graph.AddNewVertexToSource(v2, v3);
            graph.AddNewVertexToSource(v3, v5);

            graph.AddNewSourceToVertex(v5, v4);

            graph.AddNewEdgeBetween(v1, v5);

            firstLoad = v1;
            return graph;
        }

        private CustomGraph_Accessor CreateDefaultCustomGraphAccessor()
        {
            CustomVertex crap1;
            CustomVertex crap2;
            return CreateDefaultCustomGraphAccessor(out crap1, out crap2);
        }

        private CustomGraph_Accessor CreateDefaultCustomGraphAccessor(out CustomVertex firstLoad, out CustomVertex save)
        {
            var graph = new CustomGraph_Accessor();

            var v1 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v2 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v3 = new CustomVertex(OperatorFactory.GetOperator("Resize"));
            var v4 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var v5 = new CustomVertex(OperatorFactory.GetOperator("Save"));

            graph.AddNewVertex(v1);
            graph.AddNewVertexToSource(v1, v2);
            graph.AddNewVertexToSource(v2, v3);
            graph.AddNewVertexToSource(v3, v5);

            graph.AddNewSourceToVertex(v5, v4);

            graph.AddNewEdgeBetween(v1, v5);

            firstLoad = v1;
            save = v5;
            return graph;
        }

        private CustomGraph CreateTwoVerticesCustomGraph()
        {
            var graph = new CustomGraph();
            var loadVertex = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var saveVertex = new CustomVertex(OperatorFactory.GetOperator("Save"));

            graph.AddNewVertex(loadVertex);
            graph.AddNewVertexToSource(loadVertex, saveVertex);
            return graph;
        }

        private CustomGraph CreateThreeVerticesCustomGraph()
        {
            var graph = new CustomGraph();
            var loadVertex = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var appendVertex = new CustomVertex(OperatorFactory.GetOperator("Append"));
            var saveVertex = new CustomVertex(OperatorFactory.GetOperator("Save"));

            graph.AddNewVertex(loadVertex);
            graph.AddNewVertexToSource(loadVertex, appendVertex);
            graph.AddNewVertexToSource(appendVertex, saveVertex);
            return graph;
        }

        private CustomGraph CreateTwoChildsCustomGraph()
        {
            var graph = new CustomGraph();
            var loadVertex = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var appendVertex = new CustomVertex(OperatorFactory.GetOperator("Append"));
            var resizeVertex = new CustomVertex(OperatorFactory.GetOperator("Resize"));

            graph.AddNewVertex(loadVertex);
            graph.AddNewVertexToSource(loadVertex, appendVertex);
            graph.AddNewVertexToSource(loadVertex, resizeVertex);
            return graph;
        }

        private CustomGraph CreateTwoParentsCustomGraph()
        {
            var graph = new CustomGraph();
            var loadVerte1 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var loadVerte2 = new CustomVertex(OperatorFactory.GetOperator("Load"));
            var appendVertex = new CustomVertex(OperatorFactory.GetOperator("Append"));


            graph.AddNewVertex(appendVertex);
            graph.AddNewSourceToVertex(appendVertex, loadVerte1);
            graph.AddNewSourceToVertex(appendVertex, loadVerte2);
            return graph;
        }

        #endregion
    }
}
